package cn.edu.jxau.controller;

import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.edu.jxau.util.StringUtil;


/**
 * 解析service.xml，得到映射关系
 * 利用单例模式，只加载一次配置文件，将配置文件的所有的信息存入Map集合中
 * ServiceMapper
 * @author zclong 2017年8月5日
 */
public class ServiceMapper {
    private static Logger logger = LoggerFactory.getLogger(ServiceMapper.class);
    private static String configFile = "/service.xml";
    //key为ServiceConfig的name
    private static Map<String, ServiceConfig> services = new HashMap<String, ServiceConfig>(); 
    
    //会在类被加载的时候执行且仅会被执行一次，一般用来初始化静态变量和调用静态方法
    //因为在虚拟机的生命周期中一个类只被加载一次；又因为static{}是伴随类加载执行的，所以，不管你new多少次对象实例，static{}都只执行一次
    static {
        ServiceMapper.parser();
    }
    
    /**
     * 通过name获得
     * @zclong
     * @2017年8月5日
     * @ReturnType: ServiceConfig
     * @param name
     * @return
     */
    public static ServiceConfig getServiceConfig(String name) {  
        return services.get(name);
    }
    
    /**
     * 重新加载配置文件
     * @zclong
     * @2017年8月5日
     * @ReturnType: void
     */
    public static void reload() {
        synchronized (services) { //一次只有一个线程进入该代码块，防止多人同时加载配置文件
            services.clear(); //加载配置文件前先清空Map集合
            parser();
        }
    }
    
    /**
     * 解析service.xml文件
     * @zclong
     * @2017年8月10日
     * @ReturnType: void
     */
    @SuppressWarnings("unchecked")
    public static void parser() {
        SAXReader saxReader = new SAXReader();
        //path不以’/'开头时，默认是从此类所在的包下取资源；
        //path  以’/'开头时，则是从ClassPath根下获取；
        URL url = ServiceMapper.class.getResource(configFile);
        Document document;
        try {
            document = saxReader.read(url);//从xml文件获取数据  
        } catch (DocumentException e) {
            throw new RuntimeException("解析[" + url.getPath() + "]失败！\n" + e);
        }
        Element root = document.getRootElement();//获取根元素  
        
        //获取当前元素下的全部子元素即service节点  
        List<Element> eleServices = root.elements("service");
        
      //循环输出全部service的相关信息  
        for(Element eleService : eleServices) {
            String name = eleService.attributeValue("name"); //获取 name 和请求XXX.do/XXX.action对应,也和视图对应
            String className = eleService.attributeValue("class"); //class: 和处理某业务的类对应.
            //如果获取的值为空，则退出循环
            if(StringUtil.isBlank(name) || StringUtil.isBlank(className)) {
                break;
            }
            //通过构造函数进行传参
            ServiceConfig service = new ServiceConfig(name, className);
            String methodName = eleService.attributeValue("method"); //method: 和业务层的内部方法对应
            String operate = eleService.attributeValue("operate"); // 可选参数,配置SQL的操作
            if(null != methodName && !methodName.isEmpty()) {
                service.setMethodName(methodName);
            }
            if (null != operate && !operate.isEmpty()) {
                service.setOperate(operate);
            }
            //获取service的result，业务层的返回结果
            List<Element> results = eleService.elements("result");
            addResult(service, results);
            //将service添加到Map中
            services.put(service.getName(), service);
            logger.debug(service.toString());
        }
    }
        
    /**
     * 获取service.xml配置文件中的result节点下的参数，值，并封装到ServiceResultConfig中
     * @zclong
     * @2017年8月5日
     * @ReturnType: void
     * @param service
     * @param results
     */
    private static void addResult(ServiceConfig service, List<Element> results) {
        for (Element res : results) {
            //对应service.xml的result节点
            ServiceResultConfig result = new ServiceResultConfig();
            String name = res.attributeValue("name"); //name: service的method的返回结果对应
            String type = res.attributeValue("type"); //type: 重定向,转发,ajax,上传,下载的跳转方式
            if(null != name && !name.isEmpty()) {
                result.setName(name);
            }
            if(null != type && !type.isEmpty()) {
                result.setType(type);
            }
            result.setLocation(res.getStringValue()); //获取当前节点的子孙节点中所有文本内容连接成的字符串
            //添加到service
            service.addResultConfig(result);
        }
    }        
}
